package com.mingjieli.excel.form;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.yaml.snakeyaml.Yaml;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 将yaml格式的业务子表声明解析成子表的模型,以便生成Excel
 * @author mingjie
 * @since 2020-05-18
 */
class GridModelParser {

	/**
	 * 解析业务子表声明
	 * @param in 声明文件对应的InputStream
	 * @return 业务子表模型
	 */
	public List<GridModel> parse(InputStream in) {
		Yaml yaml = new Yaml();
		JSONArray statements = (JSONArray) JSON.toJSON(yaml.load(in));
		List<GridModel> gridModels = new ArrayList<>();
		for (int i = 0; i < statements.size(); i++) {
			JSONObject statement = statements.getJSONObject(i);
			gridModels.add(parseGridModel(statement));
		}

		return gridModels;
	}

	private GridModel parseGridModel(JSONObject statement) {
		String direct = statement.getString("direct");
		if (!"HORIZONTAL".equalsIgnoreCase(direct) && !"VERTICAL".equalsIgnoreCase(direct)) {
			throw new RuntimeException("解析声明文件失败: " + statement);
		}

		GridModel gridModel = new GridModel();
		gridModel.setDirect(GridModel.DIRECT.valueOf(direct.toUpperCase()));

		TitleModel rowTitleModel = toTitleModel(statement.getJSONObject("rowTitle"));
		gridModel.setRowTitle(rowTitleModel);
		TitleModel columnTitle = toTitleModel(statement.getJSONObject("columnTitle"));
		gridModel.setColumnTitle(columnTitle);

		JSONArray itemTitleList = statement.getJSONArray("itemTitleList");
		gridModel.setItemTitleList(toItemTitleModelList(itemTitleList, gridModel.getDirect()));

		return gridModel;
	}

	/**
	 * 讲每一项的标题声明转换成模型
	 * @param itemTitleList 标题声明列表
	 * @param direct 水平或竖直表格
	 * @return 标题模型列表
	 */
	private List<TitleModel> toItemTitleModelList(JSONArray itemTitleList, GridModel.DIRECT direct) {
		List<TitleModel> titleModelList = new ArrayList<>();
		if (itemTitleList == null || itemTitleList.isEmpty()) {
			return titleModelList;
		}
		TitleModel preModel = null;
		for (int i = 0; i < itemTitleList.size(); i++) {
			JSONObject titleObject = itemTitleList.getJSONObject(i);
			int startRowNum = getStartRowNum(titleObject.getInteger("startRowNum"), direct, preModel);
			int startColumnNum = getStartColumnNum(titleObject.getInteger("startColumnNum"), direct, preModel);
			int width = (titleObject.getInteger("width") == null ? 1 : titleObject.getInteger("width"));
			int height = (titleObject.getInteger("height") == null ? 1 : titleObject.getInteger("height"));

			preModel = new TitleModel(startRowNum, startColumnNum, width, height, titleObject.getString("name"));
			titleModelList.add(preModel);
		}

		return titleModelList;
	}

	/**
	 * 获取起始的行号， 若声明的起始行号不为空，直接返回；若存在preModel, 根据preModel推算，否则，异常
	 * @param startRowNum 声明中的起始行号
	 * @param direct 水平或竖直表格
	 * @param preModel 前一个标题模型
	 * @return 最终的起始行号
	 */
	private Integer getStartRowNum(Integer startRowNum, GridModel.DIRECT direct, TitleModel preModel) {
		if (startRowNum != null) {
			return startRowNum;
		}
		if (preModel == null) {
			throw new RuntimeException("第一个cell title必须指定开始行号");
		}
		return (direct == GridModel.DIRECT.HORIZONTAL ?
				preModel.getStartRowNum() :
				(preModel.getStartRowNum() + preModel.getHeight()));
	}

	/**
	 * 获取起始的列号， 若声明的起始列号不为空，直接返回；若存在preModel, 根据preModel推算，否则，异常
	 * @param startColumnNum 声明中的起始列号
	 * @param direct 水平或者竖直表格
	 * @param preModel 前一个标题模型
	 * @return 最终的起始列号
	 */
	private static Integer getStartColumnNum(Integer startColumnNum, GridModel.DIRECT direct, TitleModel preModel) {
		if (startColumnNum != null) {
			return startColumnNum;
		}
		if (preModel == null) {
			throw new RuntimeException("第一个cell title必须指定开始列号");
		}
		return direct == GridModel.DIRECT.HORIZONTAL ?
				preModel.getStartColumnNum() + preModel.getWidth() :
				preModel.getStartColumnNum();
	}

	/**
	 * 将完整的数据转换成标题的模型
	 * @param titleObject 标题模型对应的声明
	 * @return 标题模型
	 */
	private TitleModel toTitleModel(JSONObject titleObject) {
		if (titleObject == null) {
			return null;
		}

		return new TitleModel(titleObject.getInteger("startRowNum"), titleObject.getInteger("startColumnNum"),
				titleObject.getInteger("width"), titleObject.getInteger("height"), titleObject.getString("name"));
	}
}
